<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="/images/BotServices.png" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <title>Web Chat Upload to Azure Storage Demo</title>
    <script crossorigin="anonymous" src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
    <script
      src="https://unpkg.com/@azure/storage-blob@10.3.0/browser/azure-storage.blob.min.js"
      integrity="sha384-fsfhtLyVQo3L3Bh73qgQoRR328xEeXnRGdoi53kjo1uectCfAHFfavrBBN2Nkbdf"
      crossorigin="anonymous"
    ></script>

    <!-- This styling is for Web Chat demonstration purposes -->
    <style type="text/css">
      html,
      body {
        background-color: #f7f7f7;
        height: 100%;
      }

      body {
        background-image: url(/images/BotServices-Translucent.svg);
        background-position: 100% 100%;
        background-repeat: no-repeat;
        background-size: auto 50%;
        margin: 0;
      }

      #webchat {
        border-radius: 4px;
        box-shadow: 0 0 8px rgba(0, 0, 0, 0.08);
        height: calc(100% - 20px);
        left: 10px;
        overflow: hidden;
        position: fixed;
        top: 10px;
        width: 360px;
      }
    </style>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="webchat"></div>
    <script>
      // This is a helper function for fetching JSON resources.
      async function fetchJSON(url, options = {}) {
        const res = await fetch(url, {
          ...options,
          headers: {
            ...options.headers,
            accept: 'application/json'
          }
        });

        if (!res.ok) {
          throw new Error(`Failed to fetch JSON due to ${res.status}`);
        }

        return await res.json();
      }

      // Loading @azure/storage-blob bundle.
      const { Aborter, AnonymousCredential, BlockBlobURL, StorageURL, uploadBrowserDataToBlockBlob } = window.azblob;

      // Upload a file to Azure Storage by supplying an Object URL and a name.
      async function uploadFile({ name, url }) {
        // Today, Web Chat uses createObjectURL() to represent the blob.
        // https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
        // Because it is not trivial to GC the URL, we might change this behavior to use ArrayBuffer or Blob in later versions of Web Chat.
        const res = await fetch(url);

        if (!res.ok) {
          throw new Error('Cannot obtain ArrayBuffer to uploading file');
        }

        // Fetch a SAS token from the server, think of it as a permission to upload a single file to Azure Storage.
        const { sasQuery, url: uploadURL } = await fetchJSON('/api/azurestorage/uploadsastoken');

        // Uploads the file to Azure Storage directly by using a fresh SAS token from the server.
        const pipeline = StorageURL.newPipeline(new AnonymousCredential());
        const blockBlobURL = new BlockBlobURL(`${uploadURL}?${sasQuery}`, pipeline);

        // Using @azure/storage-blob bundle to upload, this function is parallelized for huge files.
        const result = await uploadBrowserDataToBlockBlob(Aborter.none, await res.arrayBuffer(), blockBlobURL, {
          metadata: { name }
        });

        // Returns the blob URL.
        return uploadURL;
      }

      (async function main() {
        const store = WebChat.createStore({}, ({ dispatch }) => next => action => {
          const { type } = action;

          // When Web Chat wants to send files, we intercept this action and send an event instead.
          if (type === 'WEB_CHAT/SEND_FILES') {
            (async function() {
              // Tells the bot that you are uploading files. This is optional.
              dispatch({ type: 'WEB_CHAT/SEND_TYPING' });

              const files = await Promise.all(action.payload.files.map(({ name, url }) => uploadFile({ name, url })));

              // Today, DirectLineJS will intercept all attachments with URLs from message activity.
              // Separating the attachment out as a multipart before sending to the bot.
              // This behavior is to facilitate the createObjectURL() calls. We might change this behavior in later versions of Web Chat.

              // In the meantime, we workaround this behavior by using an event activity.
              dispatch({
                type: 'WEB_CHAT/SEND_EVENT',
                payload: {
                  name: 'upload',
                  type: 'event',
                  value: { files }
                }
              });
            })().catch(err => console.error(err));
          } else {
            return next(action);
          }
        });

        // This is for obtaining Direct Line token from the bot.
        // In a production system, we should avoid using Direct Line secret to connect to the bot.
        const { token } = await fetchJSON('/api/directline/token');

        WebChat.renderWebChat(
          {
            directLine: WebChat.createDirectLine({ token }),
            store,
            styleOptions: {
              backgroundColor: 'rgba(255, 255, 255, .8)'
            }
          },
          document.getElementById('webchat')
        );
      })().catch(err => console.error(err));
    </script>
    <div id="root"></div>
  </body>
</html>
